home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Peter Lewis / StandardGetFolder / StandardGetFolder.p < prev    next >
Encoding:
Text File  |  1992-12-18  |  16.3 KB  |  342 lines  |  [TEXT/PJMM]

  1. {******************************************************************************}
  2. { StandardGetFolder.c                                                          }
  3. {                                                                              }
  4. {    This little chunk o' code implements a way to let the user choose a       }
  5. {    folder to save files in via a StandardFile Dialog.                        }
  6. {                                                                              }
  7. {    Since the code uses the CustomGetFile function and depends on the use of  }
  8. {    FSSpec records, it only works under System 7.0 or later.                  }
  9. {                                                                              }
  10. {    And don't forget to include the custom dialog resources ( a 'DITL' and    }
  11. {   'DLOG') in your project.                                                   }
  12. {                                                                              }
  13. {    Portions of this code were originally provided by Paul Forrester          }
  14. {    (paulf@apple.com) to the think-c internet mailing list in response to my  }
  15. {    my question on how to do exactly what this code does.  I've added a       }
  16. {    couple of features, such as the ability to handle aliased folders and     }
  17. {    the programmer definable prompt.  I also cleaned and tightened the code,  }
  18. {    stomped a couple of bugs, and packaged it up neatly.  Bunches of work,    }
  19. {    but I learned A LOT about Standard File, the File Manager, the Dialog     }
  20. {    Manager, and the Alias Manager.  I tried to include in the comments some  }
  21. {    of the neat stuff I discovered in my hours of pouring over Inside Mac.    }
  22. {    Hope you find it educational as well as useful.                           }
  23. {******************************************************************************}
  24. { Converted to Pascal by Peter N Lewis <peter@cujo.curtin.edu.au> Dec 1992 }
  25.  
  26. unit StandardGetFolder;
  27.  
  28. interface
  29.  
  30.     function GetSFCurDir: longInt;
  31.     function GetSFVRefNum: integer;
  32.     procedure StandardGetFolder (where: Point; message: Str255; var mySFReply: StandardFileReply);
  33.  
  34. implementation
  35.  
  36.     uses
  37.         Aliases, Script;
  38.  
  39. { Resource IDs }
  40.     const
  41.         rGetFolderButton = 10;
  42.         rGetFolderMessage = 11;
  43.         kFolderBit = $0010;
  44.         rGetFolderDialog = 2008;
  45.  
  46. { Global Variables }
  47.  
  48.     var
  49.         gCurrentSelectedFolder: str255;
  50.  
  51.  
  52. {******************************************************************************}
  53. { SetButtonTitle                                                               }
  54. {                                                                              }
  55. {     Whenever the selected folder is changed, SetButtonTitle is called to     }
  56. {     redraw the get folder button.  Pass it a handle to the button, the new   }
  57. {     string to be drawn in the button, and a pointer to the rect the button   }
  58. {     is drawn within.                                                         }
  59. {******************************************************************************}
  60.     procedure SetButtonTitle (ButtonHdl: Handle; name: Str255; var ButtonRect: Rect);
  61.         var
  62.             resultCode: integer;
  63.             width: integer;
  64.             TmpStr: str255;
  65.  
  66.     begin
  67.         gCurrentSelectedFolder := name;
  68.  
  69.     {*-------------------------------------------------------------------------}
  70.     { Find the width left over in the button after drawing the word 'Select'   }
  71.     { the quotation marks. Truncate the new name to this length.               }
  72.     {-------------------------------------------------------------------------*}
  73.         width := (ButtonRect.right - ButtonRect.left) - (StringWidth('Select ""J'));
  74.  
  75.         resultCode := TruncString(width, name, smTruncEnd);
  76.         if resultCode < 0 then
  77.             ;
  78.  
  79.     {*-------------------------------------------------------------------------}
  80.     { Redraw the button.                                                       }
  81.     {-------------------------------------------------------------------------*}
  82.         TmpStr := concat('Select "', name, '"');
  83.         SetCTitle(ControlHandle(ButtonHdl), TmpStr);
  84.         ValidRect(ButtonRect);
  85.     end;
  86.  
  87.  
  88. {******************************************************************************}
  89. { MyCustomGetDirectoryFileFilter                                               }
  90. {                                                                              }
  91. {     This is the file filter passed to CustomGetFile. It passes folders only. }
  92. {******************************************************************************}
  93.     function MyCustomGetDirectoryFileFilter (var myPB: CInfoPBRec; myDataPtr: Ptr): boolean;
  94.     begin
  95.         MyCustomGetDirectoryFileFilter := BAND(myPB.ioFlAttrib, kFolderBit) = 0;
  96.     end;
  97.  
  98.  
  99. {******************************************************************************}
  100. { MyCustomGetDirectoryDlogHook                                                 }
  101. {                                                                              }
  102. {     This function lets us process item hits in the GetFolderDialog.  We're   }
  103. {     only interested if the user hit the selectFolder button. We pass all     }
  104. {     other item hits back to ModalDialog.                                     }
  105. {******************************************************************************}
  106.  
  107.     function MyCustomGetDirectoryDlogHook (item: integer; theDialog: DialogPtr; myDataPtr: Ptr): integer;
  108.         type
  109.             StandardFileReplyPtr = ^StandardFileReply;
  110.         var
  111.             dlgPeek: WindowPeek;
  112.             selectedName: Str255;
  113.             pb: CInfoPBRec;
  114.             err: OSErr;
  115.             itemType: integer;
  116.             itemRect: Rect;
  117.             itemHandle: Handle;
  118.             isAlias: Boolean;
  119.             isFolder: Boolean;
  120.             mySFRPtr: StandardFileReplyPtr;
  121.     begin
  122.  
  123.     {*-------------------------------------------------------------------------}
  124.     { Set the return value to defualt to the item that was passed in.          }
  125.     {-------------------------------------------------------------------------*}
  126.         MyCustomGetDirectoryDlogHook := item;
  127.  
  128.     {*-------------------------------------------------------------------------}
  129.     { CustomGet calls dialog hook for both main and subsidiary dialog boxes.   }
  130.     { Make sure that dialog record indicates that this is the main GetFolder   }
  131.     { dialog.                                                                  }
  132.     {-------------------------------------------------------------------------*}
  133.         dlgPeek := WindowPeek(theDialog);
  134.         if OSType(dlgPeek^.refCon) = sfMainDialogRefCon then begin
  135.  
  136.         {*---------------------------------------------------------------------}
  137.         { Get a handle to the select folder button, in case we need to change  }
  138.         { the label.                                                           }
  139.         {---------------------------------------------------------------------*}
  140.             GetDItem(theDialog, rGetFolderButton, itemType, itemHandle, itemRect);
  141.  
  142.         {*---------------------------------------------------------------------}
  143.         { If this is the first time the dialog hook has been called...         }
  144.         {---------------------------------------------------------------------*}
  145.             if item = sfHookFirstCall then begin
  146.  
  147.             {*-----------------------------------------------------------------}
  148.             { Set the prompt displayed above the file list...                  }
  149.             {-----------------------------------------------------------------*}
  150.                 GetDItem(theDialog, rGetFolderMessage, itemType, itemHandle, itemRect);
  151.                 mySFRPtr := StandardFileReplyPtr(myDataPtr);
  152.                 SetIText(itemHandle, mySFRPtr^.sfFile.name);
  153.  
  154.             {*-----------------------------------------------------------------}
  155.             { And the name of the currently selected folder in the select      }
  156.             { folder button.                                                   }
  157.             {-----------------------------------------------------------------*}
  158.                 pb.ioNamePtr := @selectedName;
  159.                 pb.ioVRefNum := GetSFVRefNum;
  160.                 pb.ioDirID := GetSFCurDir;
  161.                 pb.ioFDirIndex := -1;
  162.                 err := PBGetCatInfo(@pb, FALSE);
  163.  
  164.             {*-----------------------------------------------------------------}
  165.             { Note that this error return is important! When the dialog hook   }
  166.             { is called for the first time, Super Boomerang (and possibly      }
  167.             { Norton directory assistance aren't finished doing their          }
  168.             { rebounting, so the values returned by GetSFVRefNum and           }
  169.             { GetSFCurDir may not be valid, and hence PBGetCatInfo will return }
  170.             { an error.  That one took me a while to figure out.               }
  171.             {-----------------------------------------------------------------*}
  172.                 if err <> noErr then begin
  173.                     exit(MyCustomGetDirectoryDlogHook);
  174.                 end;
  175.  
  176.                 GetDItem(theDialog, rGetFolderButton, itemType, itemHandle, itemRect);
  177.                 SetButtonTitle(itemHandle, selectedName, itemRect);
  178.             end
  179.             else begin
  180.             {*-----------------------------------------------------------------}
  181.             { Cast myDataPtr back to a SFReply pointer.                        }
  182.             {-----------------------------------------------------------------*}
  183.                 mySFRPtr := StandardFileReplyPtr(myDataPtr);
  184.  
  185.  
  186.             {*-----------------------------------------------------------------}
  187.             { If the selected folder is an alias, resolve it. isFolder will    }
  188.             { be set to true if a folder or aliased folder is selected.        }
  189.             {-----------------------------------------------------------------*}
  190.              {*-----------------------------------------------------------------}
  191.             { If the selected item is a folder or volume, just copy the name   }
  192.             { into selectedName...                                             }
  193.             {-----------------------------------------------------------------*}
  194.                 err := ResolveAliasFile(mySFRPtr^.sfFile, TRUE, isFolder, isAlias);
  195.                 if ((err = noErr) and isAlias and isFolder) or mySFRPtr^.sfIsFolder or mySFRPtr^.sfIsVolume then begin
  196.                     selectedName := mySFRPtr^.sfFile.name;
  197.  
  198.             {*-----------------------------------------------------------------}
  199.             { Otherwise, copy the name of the selected item's parent directory }
  200.             { into selectedName.                                               }
  201.             {-----------------------------------------------------------------*}
  202.                 end
  203.                 else begin
  204.  
  205.                     pb.ioNamePtr := @selectedName;
  206.                     pb.ioVRefNum := mySFRPtr^.sfFile.vRefNum;
  207.                     pb.ioDirID := mySFRPtr^.sfFile.parID;
  208.                     pb.ioFDirIndex := -1;
  209.                     err := PBGetCatInfo(@pb, FALSE);
  210.                     if err <> noErr then
  211.                         exit(MyCustomGetDirectoryDlogHook);
  212.                 end;
  213.  
  214.             {*-----------------------------------------------------------------}
  215.             { If the selected folder has changed since the last call to this   }
  216.             { dialog hook function, re-draw the button with the new selected   }
  217.             { folder name.                                                     }
  218.             {-----------------------------------------------------------------*}
  219.                 if not EqualString(selectedName, gCurrentSelectedFolder, FALSE, FALSE) then
  220.                     SetButtonTitle(itemHandle, selectedName, itemRect);
  221.  
  222.             {*-----------------------------------------------------------------}
  223.             { If the user clicked the select folder button, force a cancel and }
  224.             { set the sfGood field of the Reply record to true.                }
  225.             {-----------------------------------------------------------------*}
  226.                 if item = rGetFolderButton then begin
  227.  
  228.                     MyCustomGetDirectoryDlogHook := sfItemCancelButton;
  229.                     mySFRPtr^.sfGood := TRUE;
  230.                 end;
  231.  
  232.             end;
  233.         end;
  234.     end;
  235.  
  236.  
  237. {******************************************************************************}
  238. { StandardGetFolder                                                            }
  239. {                                                                              }
  240. {     The StandardGetFolder function. You pass it the point where you want the }
  241. {     standard file dialog box drawn, the prompt to display above the file     }
  242. {     list, and a pointer to an StandardFileReply record.                      }
  243. {                                                                              }
  244. {     Upon return, the sfFile field of the SFReply record contains the volume  }
  245. {     reference number and directory ID that specify the folder the user       }
  246. {     chose. It also passes back the name of the chosen folder.  The sfGood    }
  247. {     field is set to true if the user chose a folder, or false if not.        }
  248. {******************************************************************************}
  249.  
  250.     procedure StandardGetFolder (where: Point; message: Str255; var mySFReply: StandardFileReply);
  251.         var
  252.             theTypeList: SFTypeList;
  253.             numTypes: integer;
  254.             myModalFilter: ProcPtr;
  255.             pb: CInfoPBRec;
  256.             err: OSErr;
  257.             theItem: integer;
  258.  
  259.     begin
  260.  
  261.     {*-------------------------------------------------------------------------}
  262.     { Setting num types to -1 tells CustomGetFile to pass all files and        }
  263.     { folders to the file filter function.                                     }
  264.     {-------------------------------------------------------------------------*}
  265.         numTypes := -1;
  266.  
  267.     {*-------------------------------------------------------------------------}
  268.     { Copy the prompt to be displayed above the file list into the name field  }
  269.     { of the SFReply record. When MyCustomGetDirectoryDlogHook is called for   }
  270.     { the first time, it will use this info to draw the prompt.                }
  271.     {-------------------------------------------------------------------------*}
  272.         mySFReply.sfFile.name := message;
  273.  
  274.     {*-------------------------------------------------------------------------}
  275.     { Call CustomGetFile. Pass it a pointer to the file filter and dialog      }
  276.     { hook functions. Also pass a pointer to mySFReply in the user data field. }
  277.     {-------------------------------------------------------------------------*}
  278.         CustomGetFile(@MyCustomGetDirectoryFileFilter, numTypes, theTypeList, mySFReply, rGetFolderDialog, where, @MyCustomGetDirectoryDlogHook, nil, nil, nil, @mySFReply);
  279.  
  280.     {*-------------------------------------------------------------------------}
  281.     { Ok, now the reply record contains the volume reference number and the    }
  282.     { name of the selected folder. We need to use PBGetCatInfo to get the      }
  283.     { directory ID of the selected folder.                                     }
  284.     {-------------------------------------------------------------------------*}
  285.         if mySFReply.sfGood then begin { Don't call PBGetCatInfo on cancel! }
  286.  
  287.             pb.ioNamePtr := @mySFReply.sfFile.name;
  288.             pb.ioVRefNum := mySFReply.sfFile.vRefNum;
  289.             pb.ioFDirIndex := 0;
  290.             pb.ioDirID := mySFReply.sfFile.parID;
  291.  
  292.             err := PBGetCatInfo(@pb, FALSE);
  293.  
  294.     {*-------------------------------------------------------------------------}
  295.     { Insert your error handler here. I couldn't think of one so I left it     }
  296.     { empty. Works fine without it.                                            }
  297.     {-------------------------------------------------------------------------*}
  298.             if err <> noErr then
  299.                 ;
  300.  
  301.     {*-------------------------------------------------------------------------}
  302.     { Copy the directory ID of the selected folder to the sfFile field of the  }
  303.     { SFReply record.                                                          }
  304.     {-------------------------------------------------------------------------*}
  305.             mySFReply.sfFile.parID := pb.ioDrDirID;
  306.             mySFReply.sfFile.name := '';
  307.         end;
  308.  
  309.     end;
  310.  
  311.  
  312.  
  313. {******************************************************************************}
  314. { GetSFCurDir, GetSFVRefNum                                                    }
  315. {                                                                              }
  316. { The following set of routines are used to access a couple of low memory      }
  317. { globals that are necessary when extending Standard File.  One example is     }
  318. { trying to get the current directory while in a file filter.  These routines  }
  319. { were used to bottleneck all the low memory usage.  If the system one day     }
  320. { supports them with a trap call, then we can easily update these routines.    }
  321. {******************************************************************************}
  322.  
  323.     function GetSFCurDir: longInt;
  324.         const
  325.             CurDirStoreA = $398;
  326.         type
  327.             longPtr = ^longInt;
  328.     begin
  329.         GetSFCurDir := longPtr(CurDirStoreA)^;
  330.     end;
  331.  
  332.  
  333.     function GetSFVRefNum: integer;
  334.         const
  335.             SFSaveDiskA = $214;
  336.         type
  337.             intPtr = ^integer;
  338.     begin
  339.         GetSFVRefNum := -intPtr(SFSaveDiskA)^;
  340.     end;
  341.  
  342. end.